package com.sumer.psychology.common.persistence.redis;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;

/**
 * Created by CUIJB on 2015年5月13日
 */
public class RedisUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

    private JedisPool jedisPool;

    public RedisUtil(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 执行器 Created by CUIJB on 2015年5月7日
     */
    abstract class Executor<T> {

        Jedis jedis;
        JedisPool jedisPool;

        public Executor(JedisPool jedisPool) {
            this.jedisPool = jedisPool;
            jedis = this.jedisPool.getResource();
        }

        /**
         * 回调
         *
         * @return 执行结果
         */
        abstract T execute();

        /**
         * 调用并返回执行结果 它保证在执行execute()
         * 之后释放数据源returnResource(jedis)
         *
         * @return 执行结果
         */
        public T getResult() {
            T result = null;
            try {
                result = execute();
            } catch (Throwable e) {
                LOGGER.error("Redis execute exception", e);
                // throw new RuntimeException("Redis execute exception", e);
            } finally {
                if (jedis != null) {
                    jedisPool.returnResource(jedis);
                }
            }
            return result;
        }
    }

    /**
     * 为给定 key 设置生存时间，当 key 过期时(生存时间为 0 )，它会被自动删除。 在 Redis 中，带有生存时间的 key
     * 被称为『可挥发』(volatile)的。
     *
     * @param key    key
     * @param expire 生命周期，单位为秒
     * @return 1: 设置成功 0: 已经超时或key不存在
     */
    public Long expire(final String key, final int expire) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.expire(key, expire);
            }
        }.getResult();
    }

    /**
     * 删除
     *
     * @param key 匹配的key
     * @return 删除成功的条数
     */
    public Long delKey(final String key) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.del(key);
            }
        }.getResult();
    }

	/* ==========================Strings========================== */

    public String setString(final String key, final String value) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                return jedis.set(key, value);
            }
        }.getResult();
    }

    public String setString(final String key, final String value, final int expire) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                return jedis.setex(key, expire, value);
            }
        }.getResult();
    }

    public String getString(final String key) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                return jedis.get(key);
            }
        }.getResult();
    }

    /**
     * 进行加1操作
     *
     * @param key
     * @return
     */
    public Long stringIncr(final String key) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.incr(key);
            }
        }.getResult();
    }

    public Long stringIncrBy(final String key, final long num) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.incrBy(key, num);
            }
        }.getResult();
    }
    /* ==========================Hashes========================== */

    public Long hashSet(final String key, final String field, final String value) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.hset(key, field, value);
            }
        }.getResult();
    }

    public Long hashSet(final String key, final String field, final String value, final int expire) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                Pipeline pipeline = jedis.pipelined();
                Response<Long> result = pipeline.hset(key, field, value);
                pipeline.expire(key, expire);
                pipeline.sync();
                return result.get();
            }
        }.getResult();
    }

    public String hashGet(final String key, final String field) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                return jedis.hget(key, field);
            }
        }.getResult();
    }

    public String hashGet(final String key, final String field, final int expire) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                Pipeline pipeline = jedis.pipelined();
                Response<String> result = pipeline.hget(key, field);
                pipeline.expire(key, expire);
                pipeline.sync();
                return result.get();
            }
        }.getResult();
    }

    public String hashMultipleSet(final String key, final Map<String, String> hash) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                return jedis.hmset(key, hash);
            }
        }.getResult();
    }

    public String hashMultipleSet(final String key, final Map<String, String> hash, final int expire) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                Pipeline pipeline = jedis.pipelined();
                Response<String> result = pipeline.hmset(key, hash);
                pipeline.expire(key, expire);
                pipeline.sync();
                return result.get();
            }
        }.getResult();
    }

    public List<String> hashMultipleGet(final String key, final String... fields) {
        return new Executor<List<String>>(jedisPool) {
            @Override
            List<String> execute() {
                return jedis.hmget(key, fields);
            }
        }.getResult();
    }

    public Map<String, String> hashGetAll(final String key) {
        return new Executor<Map<String, String>>(jedisPool) {

            @Override
            Map<String, String> execute() {
                return jedis.hgetAll(key);
            }
        }.getResult();
    }

	/* ==========================List========================== */

    public List<String> listGetAll(final String key) {
        return new Executor<List<String>>(jedisPool) {
            @Override
            List<String> execute() {
                return jedis.lrange(key, 0, -1);
            }
        }.getResult();
    }

    public List<String> listRange(final String key, final long beginIndex, final long endIndex) {
        return new Executor<List<String>>(jedisPool) {
            @Override
            List<String> execute() {
                return jedis.lrange(key, beginIndex, endIndex - 1);
            }
        }.getResult();
    }

    /**
     * 将一个或多个值 value 插入到列表 key 的表头, 当列表大于指定长度是就对列表进行修剪(trim)
     *
     * @param key   key
     * @param value string value
     * @param size  链表超过这个长度就修剪元素
     * @return 执行 listPushHeadAndTrim 命令后，列表的长度。
     */
    public Long listPushHeadAndTrim(final String key, final String value, final long size) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                Pipeline pipeline = jedis.pipelined();
                Response<Long> result = pipeline.lpush(key, value);
                // 修剪列表元素, 如果 size - 1 比 end 下标还要大，Redis将 size 的值设置为 end 。
                pipeline.ltrim(key, 0, size - 1);
                pipeline.sync();
                return result.get();
            }
        }.getResult();
    }

    public Long listPushHead(final String key, final String value) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.lpush(key, value);
            }
        }.getResult();
    }

    public Long listPushTail(final String key, final String... values) {
        return new Executor<Long>(jedisPool) {
            @Override
            Long execute() {
                return jedis.rpush(key, values);
            }
        }.getResult();
    }
}
